﻿//------------------------------------------------------------------------------
// <copyright file="RpcHookCode.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

namespace Microsoft.Internal.GamesTest.Rpc.Client
{
    using System;
    using System.Globalization;

    /// <summary>
    /// Represents the hook error code sent back in the response message.
    /// </summary>
    public struct RpcHookCode : IEquatable<RpcHookCode>
    {
        /// <summary>
        /// Represents a success HRESULT of 0.
        /// </summary>
        public static readonly RpcHookCode Success = new RpcHookCode();

        /// <summary>
        /// Bit representing a failure severity in HRESULT.
        /// </summary>
        private const uint FailureBit = 1;

        /// <summary>
        /// The facility in an HRESULT code represents the source of the failure.
        /// Custom facility codes can have a value of 257-4095.
        /// We are using a facility code of 500 for use with hooks.
        /// </summary>
        private const uint HookFacility = 0x1F4;

        /// <summary>
        /// Initializes a new instance of the <see cref="RpcHookCode"/> struct.
        /// The provided <paramref name="code"/> will be OR'd with a severity
        /// bit and a hook facility number in order to create the equivalent
        /// of an HRESULT used in the native side.
        /// </summary>
        /// <param name="code">Code used to form the RpcHookCode.</param>
        public RpcHookCode(ushort code)
            : this()
        {
            this.Code = (int)((FailureBit << 31) |
                              (HookFacility << 16) |
                               code);
        }

        /// <summary>
        /// Gets the code stored by this <see cref="RpcHookCode"/>.
        /// </summary>
        public int Code { get; private set; }

        /// <summary>
        /// Overload of the equals operator to compare if two codes have
        /// the same value as opposed to the same reference.
        /// </summary>
        /// <param name="code1">First <see cref="RpcHookCode"/> to compare.</param>
        /// <param name="code2">Second <see cref="RpcHookCode"/> to compare.</param>
        /// <returns>True if both objects represent the same code value.</returns>
        public static bool operator ==(RpcHookCode code1, RpcHookCode code2)
        {
            return code1.Equals(code2);
        }

        /// <summary>
        /// Overload of the not equals operator to compare if two codes have
        /// a different value as opposed to a different reference.
        /// </summary>
        /// <param name="code1">First <see cref="RpcHookCode"/> to compare.</param>
        /// <param name="code2">Second <see cref="RpcHookCode"/> to compare.</param>
        /// <returns>True if both objects represent different code values.</returns>
        public static bool operator !=(RpcHookCode code1, RpcHookCode code2)
        {
            return !code1.Equals(code2);
        }

        /// <summary>
        /// Overload of the IEquatable.Equals method. Compares if two codes have
        /// the same value as opposed to the same reference.
        /// </summary>
        /// <param name="other">Object to compare against.</param>
        /// <returns>True if both objects represent the same code value.</returns>
        public bool Equals(RpcHookCode other)
        {
            return this.Code == other.Code;
        }

        /// <summary>
        /// Overload of the Object.Equals method. Compares if two codes have
        /// the same value as opposed to the same reference.
        /// </summary>
        /// <param name="obj">Object to compare against.</param>
        /// <returns>True if both objects represent the same code value.</returns>
        public override bool Equals(object obj)
        {
            if (obj is RpcHookCode)
            {
                return this.Equals((RpcHookCode)obj);
            }

            return false;
        }

        /// <summary>
        /// Gets a hash value for this instance.
        /// </summary>
        /// <returns>The hash value for this instance.</returns>
        public override int GetHashCode()
        {
            return this.Code.GetHashCode();
        }

        /// <summary>
        /// Overrides ToString to return the code represented by this object.
        /// </summary>
        /// <returns>Code represented by this object in hexadecimal format.</returns>
        public override string ToString()
        {
            return string.Format(CultureInfo.InvariantCulture, "0x{0:X}", this.Code);
        }

        /// <summary>
        /// Creates an <see cref="RpcHookCode"/> from an int by
        /// setting the member code directly.
        /// </summary>
        /// <param name="code">Code to use to create object.</param>
        /// <returns>Created <see cref="RpcHookCode"/>.</returns>
        /// <remarks>
        /// Users should only be allowed to specify the last 16 bits to
        /// avoid overriding TDK RPC or System error codes, and to
        /// correctly map the native-side codes.
        /// This method is added to compare against the regular HRESULT
        /// returned by RpcResponse.
        /// </remarks>
        internal static RpcHookCode CreateFromInt(int code)
        {
            RpcHookCode rpcHookCode = new RpcHookCode();
            rpcHookCode.Code = code;
            return rpcHookCode;
        }
    }
}
